/*
 *  Copyright 2012 Dirk Vranckaert
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package eu.vranckaert.vvl.model;

import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import eu.vranckaert.vvl.utils.context.Constants;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;

/**
 * User: DIRK VRANCKAERT
 * Date: 28/02/12
 * Time: 15:27
 */
public class TrafficInfoAlarm implements Comparable<TrafficInfoAlarm>{
    private static final SimpleDateFormat dateformatYYYYMMDDHHMM = new SimpleDateFormat("yyyyMMddHHmm");

    private Calendar alarmTime;
    private List<Constants.WeekDays> repeatingDays = new ArrayList<Constants.WeekDays>();
    private TypeTrafficInfoEnum typeTrafficInfo;
    private List<Integer> alarmIds = new ArrayList<Integer>();

    //Graphical
    private TrafficInfoGraphicalEnum.Region graphicalRegion;

    //Text
    private ArrayList<TrafficInfoTextEnum.InformationType> textInfoTypes = new ArrayList<TrafficInfoTextEnum.InformationType>();
    private ArrayList<TrafficInfoTextEnum.Region> textRegions = new ArrayList<TrafficInfoTextEnum.Region>();
    private ArrayList<TrafficInfoTextEnum.Highroad> textHighroads = new ArrayList<TrafficInfoTextEnum.Highroad>();
    private ArrayList<TrafficInfoTextEnum.SecondaryRoads> textSecondaryRoads = new ArrayList<TrafficInfoTextEnum.SecondaryRoads>();

    public Calendar getAlarmTime() {
        return alarmTime;
    }

    public void setAlarmTime(Calendar alarmTime) {
        this.alarmTime = alarmTime;
    }

    public List<Constants.WeekDays> getRepeatingDays() {
        return repeatingDays;
    }

    public void setRepeatingDays(List<Constants.WeekDays> repeatingDays) {
        this.repeatingDays = repeatingDays;
    }

    public TypeTrafficInfoEnum getTypeTrafficInfo() {
        return typeTrafficInfo;
    }

    public void setTypeTrafficInfo(TypeTrafficInfoEnum typeTrafficInfo) {
        this.typeTrafficInfo = typeTrafficInfo;
    }

    public List<Integer> getAlarmIds() {
        return alarmIds;
    }

    public void setAlarmIds(List<Integer> alarmIds) {
        this.alarmIds = alarmIds;
    }

    public TrafficInfoGraphicalEnum.Region getGraphicalRegion() {
        return graphicalRegion;
    }

    public void setGraphicalRegion(TrafficInfoGraphicalEnum.Region graphicalRegion) {
        this.graphicalRegion = graphicalRegion;
    }

    public ArrayList<TrafficInfoTextEnum.InformationType> getTextInfoTypes() {
        return textInfoTypes;
    }

    public void setTextInfoTypes(ArrayList<TrafficInfoTextEnum.InformationType> textInfoTypes) {
        this.textInfoTypes = textInfoTypes;
    }

    public ArrayList<TrafficInfoTextEnum.Region> getTextRegions() {
        return textRegions;
    }

    public void setTextRegions(ArrayList<TrafficInfoTextEnum.Region> textRegions) {
        this.textRegions = textRegions;
    }

    public ArrayList<TrafficInfoTextEnum.Highroad> getTextHighroads() {
        return textHighroads;
    }

    public void setTextHighroads(ArrayList<TrafficInfoTextEnum.Highroad> textHighroads) {
        this.textHighroads = textHighroads;
    }

    public ArrayList<TrafficInfoTextEnum.SecondaryRoads> getTextSecondaryRoads() {
        return textSecondaryRoads;
    }

    public void setTextSecondaryRoads(ArrayList<TrafficInfoTextEnum.SecondaryRoads> textSecondaryRoads) {
        this.textSecondaryRoads = textSecondaryRoads;
    }
    
    public boolean isRepeatingAlarm() {
        if (repeatingDays == null || repeatingDays.isEmpty()) {
            return false;
        }
        return true;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof TrafficInfoAlarm)) return false;

        TrafficInfoAlarm that = (TrafficInfoAlarm) o;

        if (alarmIds != null ? !alarmIds.equals(that.alarmIds) : that.alarmIds != null)
            return false;

        return true;
    }

    @Override
    public int hashCode() {
        return alarmIds != null ? alarmIds.hashCode() : 0;
    }

    private static final String SEPARATOR_VAR = "#";
    private static final String SEPARATOR_VAR_LIST_VALUES = ";";

    @Override
    /**
     * Returns the alarm in two possible types:
     * Graphical alarms: HOUR:MINUTES|REPEATING_DAYS|ALARM_IDS|TYPE|REGION
     * Text alarms: HOUR:MINUTES|REPEATING_DAYS|ALARM_IDS|TYPE|INFOS|REGIONS|HIGHROADS|SECONDARY_ROADS
     */
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append(dateformatYYYYMMDDHHMM.format(alarmTime.getTime()));
        sb.append(SEPARATOR_VAR);
        for (Constants.WeekDays weekDay : repeatingDays) {
            sb.append(weekDay.toString());
            sb.append(SEPARATOR_VAR_LIST_VALUES);
        }
        if (sb.charAt(sb.length()-1) == SEPARATOR_VAR_LIST_VALUES.charAt(0)) {
            sb.deleteCharAt(sb.length() - 1);
        }
        sb.append(SEPARATOR_VAR);
        for (int id : alarmIds) {
            sb.append(id);
            sb.append(SEPARATOR_VAR_LIST_VALUES);
        }
        if (sb.charAt(sb.length()-1) == SEPARATOR_VAR_LIST_VALUES.charAt(0)) {
            sb.deleteCharAt(sb.length()-1);
        }
        sb.append(SEPARATOR_VAR);
        sb.append(typeTrafficInfo.toString());
        sb.append(SEPARATOR_VAR);

        switch (typeTrafficInfo) {
            case GRAPHICAL: {
                sb.append(graphicalRegion.getValue());
                break;
            }
            case TEXT: {
                for (TrafficInfoTextEnum.InformationType infoType : textInfoTypes) {
                    sb.append(infoType.getValue());
                    sb.append(SEPARATOR_VAR_LIST_VALUES);
                }
                if (sb.charAt(sb.length()-1) == SEPARATOR_VAR_LIST_VALUES.charAt(0)) {
                    sb.deleteCharAt(sb.length() - 1);
                }
                sb.append(SEPARATOR_VAR);
                for (TrafficInfoTextEnum.Region region : textRegions) {
                    sb.append(region.getValue());
                    sb.append(SEPARATOR_VAR_LIST_VALUES);
                }
                if (sb.charAt(sb.length()-1) == SEPARATOR_VAR_LIST_VALUES.charAt(0)) {
                    sb.deleteCharAt(sb.length() - 1);
                }
                sb.append(SEPARATOR_VAR);
                for (TrafficInfoTextEnum.Highroad highroad : textHighroads) {
                    sb.append(highroad.getValue());
                    sb.append(SEPARATOR_VAR_LIST_VALUES);
                }
                if (sb.charAt(sb.length()-1) == SEPARATOR_VAR_LIST_VALUES.charAt(0)) {
                    sb.deleteCharAt(sb.length() - 1);
                }
                sb.append(SEPARATOR_VAR);
                for (TrafficInfoTextEnum.SecondaryRoads secondaryRoad : textSecondaryRoads) {
                    sb.append(secondaryRoad.getValue());
                    sb.append(SEPARATOR_VAR_LIST_VALUES);
                }
                if (sb.charAt(sb.length()-1) == SEPARATOR_VAR_LIST_VALUES.charAt(0)) {
                    sb.deleteCharAt(sb.length() - 1);
                }
                break;
            }
        }

        return sb.toString();
    }

    /**
     * Creates an alarm from it's String representation.
     * @param alarmToString The String representation of an alarm.
     * @return The alarm.
     */
    public static TrafficInfoAlarm fromString(String alarmToString) {
        if (alarmToString == null || alarmToString.length() == 0) {
            return null;
        }

        TrafficInfoAlarm alarm = new TrafficInfoAlarm();
        
        String[] alarmVars = alarmToString.split(SEPARATOR_VAR);

        if ( alarmVars.length < 5 && alarmVars.length > 8 ) {
            // TODO Wrap in a VVL exception
        }
        
        // Get repeating days
        alarm.setRepeatingDays(new ArrayList<Constants.WeekDays>());
        String[] repeatingDays = alarmVars[1].split(SEPARATOR_VAR_LIST_VALUES);
        for (String repeatingDay : repeatingDays) {
            if (repeatingDay != null && repeatingDay.length() != 0) {
                Constants.WeekDays day = Constants.WeekDays.valueOf(repeatingDay);
                alarm.getRepeatingDays().add(day);
            }
        }

        // Get alarm time based on repeating alarm or not...
        Calendar parsedCalendar = Calendar.getInstance();
        try {
            parsedCalendar.setTime(dateformatYYYYMMDDHHMM.parse(alarmVars[0]));
        } catch (ParseException e) {}
        Calendar calendar = Calendar.getInstance();
        if (alarm.isRepeatingAlarm()) {
            calendar.set(Calendar.HOUR_OF_DAY, parsedCalendar.get(Calendar.HOUR_OF_DAY));
            calendar.set(Calendar.MINUTE, parsedCalendar.get(Calendar.MINUTE));
        } else {
            calendar.setTime(parsedCalendar.getTime());
        }
        alarm.setAlarmTime(calendar);
        
        // Get the alarm ids
        alarm.setAlarmIds(new ArrayList<Integer>());
        String [] alarmIds = alarmVars[2].split(SEPARATOR_VAR_LIST_VALUES);
        for (String alarmId : alarmIds) {
            alarm.getAlarmIds().add(Integer.parseInt(alarmId));
        }
        
        // Get the type
        TypeTrafficInfoEnum type = TypeTrafficInfoEnum.valueOf(alarmVars[3]);
        alarm.setTypeTrafficInfo(type);
        
        switch (type) {
            case GRAPHICAL: {
                // Graphical region
                TrafficInfoGraphicalEnum.Region region = TrafficInfoGraphicalEnum.Region.getByValue(alarmVars[4]);
                alarm.setGraphicalRegion(region);
                break;
            }
            case TEXT: {
                // Text info type
                String[] infoTypes = alarmVars[4].split(SEPARATOR_VAR_LIST_VALUES);
                alarm.setTextInfoTypes(new ArrayList<TrafficInfoTextEnum.InformationType>());
                for (String infoType : infoTypes) {
                    TrafficInfoTextEnum.InformationType infoTypeByValue = TrafficInfoTextEnum.InformationType.getByValue(infoType);
                    if (infoTypeByValue != null)
                        alarm.getTextInfoTypes().add(infoTypeByValue);
                }

                // Text region
                String[] regions = alarmVars[5].split(SEPARATOR_VAR_LIST_VALUES);
                alarm.setTextRegions(new ArrayList<TrafficInfoTextEnum.Region>());
                for (String region : regions) {
                    TrafficInfoTextEnum.Region regionByValue = TrafficInfoTextEnum.Region.getByValue(Integer.parseInt(region));
                    if (regionByValue != null)
                        alarm.getTextRegions().add(regionByValue);
                }


                if (alarmVars.length > 6) {
                    // Text highroads
                    String[] highroads = alarmVars[6].split(SEPARATOR_VAR_LIST_VALUES);
                    alarm.setTextHighroads(new ArrayList<TrafficInfoTextEnum.Highroad>());
                    for (String highroad : highroads) {
                        TrafficInfoTextEnum.Highroad highroadByValue = TrafficInfoTextEnum.Highroad.getByValue(Integer.parseInt(highroad));
                        if (highroadByValue != null)
                            alarm.getTextHighroads().add(highroadByValue);
                    }

                    if (alarmVars.length > 7) {
                        // Text secondary roads
                        String[] secondaryRoads = alarmVars[7].split(SEPARATOR_VAR_LIST_VALUES);
                        alarm.setTextSecondaryRoads(new ArrayList<TrafficInfoTextEnum.SecondaryRoads>());
                        for (String secondaryRoad : secondaryRoads) {
                            TrafficInfoTextEnum.SecondaryRoads secondaryRoadByValue = TrafficInfoTextEnum.SecondaryRoads.getByValue(Integer.parseInt(secondaryRoad));
                            if (secondaryRoadByValue != null)
                                alarm.getTextSecondaryRoads().add(secondaryRoadByValue);
                        }
                    }
                }
                break;
            }
        }
        
        return alarm;
    }

    /**
     * Get a pending intent for a certain alarm id.
     * @param ctx The context.
     * @param alarmId The alarm id.
     * @return The pending intent for this alarm id.
     */
    public PendingIntent getPendingIntent(Context ctx, int alarmId) {
        Intent intent = new Intent(ctx, getTypeTrafficInfo().getActivityClass());
        switch (getTypeTrafficInfo()) {
            case GRAPHICAL: {
                intent.putExtra(Constants.Extra.REGION, getGraphicalRegion());
                break;
            }
            case TEXT: {
                intent.putExtra(Constants.Extra.INFORMATION_TYPES, getTextInfoTypes());
                intent.putExtra(Constants.Extra.REGIONS, getTextRegions());
                intent.putExtra(Constants.Extra.HIGHROADS, getTextHighroads());
                intent.putExtra(Constants.Extra.SECONDARY_ROADS, getTextSecondaryRoads());
                break;
            }
        }

        PendingIntent pendingIntent = PendingIntent.getActivity(ctx, alarmId, intent, PendingIntent.FLAG_ONE_SHOT);
        
        return pendingIntent;
    }

    /**
     * Get a list of all the pending intents for this alarm.
     * @param ctx The context.
     * @return The list of {@link PendingIntent} instances for this alarm.
     */
    public List<PendingIntent> getPendingIntents(Context ctx) {
        List<PendingIntent> pendingIntents = new ArrayList<PendingIntent>();
        
        for (int alarmId : getAlarmIds()) {
            pendingIntents.add(getPendingIntent(ctx, alarmId));
        }
        
        return pendingIntents;
    }

    @Override
    public int compareTo(TrafficInfoAlarm trafficInfoAlarm) {
        if ( (this.isRepeatingAlarm() && trafficInfoAlarm.isRepeatingAlarm()) || (!this.isRepeatingAlarm() && !trafficInfoAlarm.isRepeatingAlarm()) ) {
            // Two repeating alarms OR two single-shot alarms...
            return this.getAlarmTime().compareTo(trafficInfoAlarm.getAlarmTime());
        } else if (!this.isRepeatingAlarm() && trafficInfoAlarm.isRepeatingAlarm()) {
            // This is single-shot, compared is repeating alarm...
            return -1;
        } else if (this.isRepeatingAlarm() && !trafficInfoAlarm.isRepeatingAlarm()) {
            // This is repeating, compared is single-shot alarm...
            return 1;
        }

        return 0;
    }
}
